﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace System.Windows.Controls
{
    /// <summary>
    /// Implements a view control which stacks items vertically in a <see cref="ScrollViewer"/>. This control also provides
    /// animation support for its items.
    /// </summary>
    public class StackView : System.Windows.Controls.SelectionItemsControl
    {
        /// <summary>
        /// Initializes an instance of the <see cref="StackView"/> instance.
        /// </summary>
        public StackView()
        {
            this.DefaultStyleKey = typeof(StackView);
            this.UseItemAnimation = true;
        }

        /// <summary>
        /// Identifies the <see cref="UseItemAnimation"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty UseItemAnimationProperty = RegisterDependencyProperty(typeof(StackView), "UseItemAnimation", typeof(bool));

        /// <summary>
        /// Provides an overriden implementation for dispatching dependency proerty changes.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnDependencyPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.Property == UseItemAnimationProperty)
            {
                this.UseItemAnimation = (bool)e.NewValue;
            }
            else
            {
                base.OnDependencyPropertyChanged(e);
            }
        }

        /// <summary>
        /// Gets/Sets a boolean value indicating whether items should be animated when they are data bound.
        /// </summary>
        public bool UseItemAnimation
        {
            get { return (bool)GetValue(UseItemAnimationProperty); }
            set { SetValue(UseItemAnimationProperty, value); }
        }

        /// <summary>
        /// (Overridden) Creates a <see cref="StackViewItem"/> instance for use in the <see cref="StackView"/> control.
        /// </summary>
        /// <returns>A <see cref="System.Windows.DependencyObject"/> instance</returns>
        protected override DependencyObject GetContainerForItemOverride()
        {
            return new StackViewItem();
        }

        /// <summary>
        /// (Overridden) Specifies whether a data bound item is the proper container type.
        /// </summary>
        /// <returns>A boolean value indicating whether the item is the proper container (true) or not (false)</returns>
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return (item is StackViewItem);
        }

        /// <summary>
        /// (Overridden) Prepares a data bound item for insertion into the <see cref="StackView"/> control.
        /// </summary>
        /// <param name="element">The container element - a <see cref="StackViewItem"/> instance</param>
        /// <param name="item">The data bound item associated with the container element</param>
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);
            if (element is StackViewItem)
            {
                StackViewItem  viewItem = (StackViewItem)element;
                viewItem.enableAnimation = this.UseItemAnimation;
                viewItem.Opacity = (this.UseItemAnimation ? 0 : 1);
            }
        }
    }

    /// <summary>
    /// Implements an item control for use in the <see cref="StackView"/> control.
    /// </summary>
    public class StackViewItem : System.Windows.Controls.ItemControl
    {
        /// <summary>
        /// Instantiates a <see cref="StackViewItem"/> instance.
        /// </summary>
        public StackViewItem()
        {
            this.DefaultStyleKey = typeof(StackViewItem);

            scale = new ScaleTransform();
            this.RenderTransform = scale;

            //Center Reveals
            revealStory = Animation.CreateStoryboard(TimeSpan.FromSeconds(0.5));
            scaleY = Animation.CreateAnimation(revealStory, scale, "ScaleY", 0, 0);
            revealStory.Children.Add(scaleY);
            fadeAnim = Animation.CreateAnimation(revealStory, this, "Opacity", 0, 0);
            revealStory.Children.Add(fadeAnim);
            this.Resources.Add(revealStory);
        }

        private Storyboard          revealStory;
        private DoubleAnimation     scaleY;
        private DoubleAnimation     fadeAnim;
        private ScaleTransform      scale;

        internal bool               enableAnimation = true;

        /// <summary>
        /// (Override) Provides a new implementation for ArrangeOverride such that animation transforms are properly centered.
        /// </summary>
        /// <param name="finalSize">The final size for the control</param>
        /// <returns></returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            Size size = base.ArrangeOverride(finalSize);

            scale.CenterX = this.ActualWidth / 2;
            scale.CenterY = this.ActualHeight / 2;

            if (enableAnimation)
            {
                scaleY.From = 0;
                scaleY.To = 1;
                fadeAnim.From = 0;
                fadeAnim.To = 1;

                revealStory.Begin();
                enableAnimation = false;
            }

            return size;
        }
    }
}
